/**
 * User: alesj
 * Date: 2005.2.23
 * Time: 13:24:42
 * 
 * (C) Genera Lynx d.o.o.
 */

package com.generalynx.common.web.tags;

import org.apache.commons.jxpath.JXPathContext;
import org.hibernate.Hibernate;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.web.util.TagUtils;

import javax.servlet.jsp.JspException;
import java.util.Collection;
import java.util.Iterator;

public class NodeTag extends CommonWriteTag implements IRootable {

    //conts
    public static final String BUTTON_NONE = "none";
    public static final String BUTTON_HIDDEN = "hidden";
    public static final String BUTTON_CHECKBOX = "checkbox";
    public static final String BUTTON_RADIO = "radio";

    //ui
    private String check = BUTTON_NONE;
    private String labelProperty;

    //collection or xpath
    private String collectionProperty;
    private String xpath;

    //id
    private String idProperty = "id";

    //leaf
    private boolean leaf;

    //string array to write to on formObject
    private String array;

    //current iterator and node
    private Iterator iterator;
    private Object node;
    private int counter; //used to differentiate same nodes on same level
    private long creationTime = System.currentTimeMillis();

    public int doStartTag() throws JspException {
        IRootable rootable = (IRootable)getParent();
        Object rootObject = rootable.getRoot();

        if (collectionProperty == null && xpath == null) {
            throw new JspException("Collection property or xpath must be set!");
        }
        if (collectionProperty != null) {
            BeanWrapper beanWrapper = new BeanWrapperImpl(rootObject);
            Collection collection = (Collection)beanWrapper.getPropertyValue(collectionProperty);
            iterator = collection.iterator();
        } else {
            iterator = JXPathContext.newContext(rootObject).iterate(xpath);
        }

        if (getOutputConditionPluginName() != null) {
            conditionPlugin = (ITagOutputConditionPlugin)pageContext.getAttribute(getOutputConditionPluginName(),
                                                                                  TagUtils.getScope(TagUtils.SCOPE_APPLICATION));
        }

        if (leaf) {
            boolean rootableCondition = rootable.getConditionPlugin().outputNode(rootObject);
            while(rootableCondition && iterator.hasNext()) {
                node = iterator.next();
                createOutput();
            }
            return SKIP_BODY;
        }
        if (iterator.hasNext()) {
            node = iterator.next();
            createOutput();
            return EVAL_BODY_INCLUDE;
        } else {
            return SKIP_BODY;
        }
    }

    public int doAfterBody() throws JspException {
        if (!leaf && iterator.hasNext()) {
            if (conditionPlugin.outputNode(node)) {
                write("</div>\n</div>"); //close previous brother node
            }
            node = iterator.next();
            createOutput();
            return EVAL_BODY_AGAIN;
        } else {
            return SKIP_BODY;
        }
    }

    public int doEndTag() throws JspException {
        if (!leaf && conditionPlugin.outputNode(node)) {
            write("</div>\n</div>"); //close last previous brother node
        }
        return EVAL_PAGE;
    }

    public Object getRoot() {
        return node;
    }

    private void createOutput() throws JspException {
        BeanWrapper beanWrapper = new BeanWrapperImpl(node);

        Integer treeID = (Integer) pageContext.getAttribute(TreeTag.TREE_ID);
        String nodeIdentString = getNodeClass().getName() + "+" + beanWrapper.getPropertyValue(idProperty);
        String maskIdent = MaskGenerator.mask(treeID.intValue(), nodeIdentString);
        String nodeKey = getLevel() + "X" + (counter++) + "X" + creationTime;
        String curId = treeID + "_" + nodeKey;
        String cbId = "inputChk" + curId;
        String childrenCollapsed = leaf ? "" : " style=\"display: none\"";

        String subDivStart = "<div class=\"subtree\">\n";
        Object plus = pageContext.getAttribute(TreeTag.PLUS);
        Object minus = pageContext.getAttribute(TreeTag.MINUS);
        String img = "<img alt=\"\" class=\"triangle\" id=\"img" + curId + "\" " +
                          "onclick=\"clickPM(" + treeID + ",'" + nodeKey + "', '" + plus + "', '" + minus + "')\"" +
                          "src=\"" + plus + "\" />";

        String inputChk = null;
        if (!BUTTON_NONE.equalsIgnoreCase(check)) {
            String arrayString = (array == null) ? (leaf ? "leaves" : nodeKey) : array;
            String checked = (isChecked(beanWrapper)) ? "checked=\"checked\"" : "";
            String hide = BUTTON_HIDDEN.equalsIgnoreCase(check) ? " style=\"display:none\"" : "";
            inputChk = "<input type=\"" + getChkBoxType() + "\" " + checked + " name=\"" + arrayString + "\" id=\"" + cbId + "\" value=\"" + maskIdent + "\"" + hide + " />";
        }

        String labelStart = "<label for =\"" + cbId + "\">"; // minor bug: label not needed when there is no checkbox / radio button
        String span = "<span class=\"nodetext\" onclick=\"selectNode(" + treeID + ",'" + nodeKey + "')\">" + beanWrapper.getPropertyValue(labelProperty) + "</span>";
        String labelEnd = "</label>\n";
        String childrenDiv = "<div class=\"children\" id=\"div" + curId + "\"" + childrenCollapsed + ">\n";
        String subDivEnd = "</div>";

        //writting to page
        if (conditionPlugin.outputNode(node)) {
            write(subDivStart);

            if (!leaf) write(img);
            if (!BUTTON_NONE.equalsIgnoreCase(check)) {
                write(inputChk);
            }
            if (leaf) write(labelStart);
            write(span);
            if (leaf) write(labelEnd);
            if (!leaf) write(childrenDiv);
            if (leaf) write(subDivEnd);
        }
    }

    private boolean isChecked(BeanWrapper beanWrapper) {
        try {
            Boolean checked = (Boolean)beanWrapper.getPropertyValue("checked");
            return checked.booleanValue();
        } catch (BeansException e) {
            return false;
        }
    }

    private String getChkBoxType() {
        return (BUTTON_HIDDEN.equalsIgnoreCase(check)) ? BUTTON_CHECKBOX : check;
    }

    private Class getNodeClass() {
        return Hibernate.getClass(node);
    }

    // --------------------------------------------------------------- //

    public void setCheck(String check) {
        this.check = check;
    }

    public void setLabelProperty(String labelProperty) {
        this.labelProperty = labelProperty;
    }

    public void setCollectionProperty(String collectionProperty) {
        this.collectionProperty = collectionProperty;
    }

    public void setXpath(String xpath) {
        this.xpath = xpath;
    }

    public void setIdProperty(String idProperty) {
        this.idProperty = idProperty;
    }

    public void setLeaf(boolean leaf) {
        this.leaf = leaf;
    }

    public void setArray(String array) {
        this.array = array;
    }
}
